﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConfigurationDemo
{
    //配置Key辅助类
    public static class ConfigurationPath
    {
        public static readonly string KeyDelimiter = ":";
        //配置段合并 ["person","name"] 合并之后 为 "person:name"
        public static string Combine(params string[] pathSegments)
        {
            return string.Join(KeyDelimiter, pathSegments);
        }
        //配置段合并 ["person","name"] 合并之后 为 "person:name"
        public static string Combine(IEnumerable<string> pathSegments)
        {
            return string.Join(KeyDelimiter, pathSegments);
        }
        //获取配置段的名称，假设path为"person:name"，则返回name
        public static string? GetSectionKey(string? path)
        {
            if (string.IsNullOrEmpty(path))
            {
                return path;
            }

            int lastDelimiterIndex = path.LastIndexOf(':');
            return lastDelimiterIndex < 0 ? path : path.Substring(lastDelimiterIndex + 1);
        }
        //获取配置段的父路径，假设path为"person:name"，则返回person
        public static string? GetParentPath(string? path)
        {
            if (string.IsNullOrEmpty(path))
            {
                return null;
            }

            int lastDelimiterIndex = path.LastIndexOf(':');
            return lastDelimiterIndex < 0 ? null : path.Substring(0, lastDelimiterIndex);
        }
    }

    //比较配置的的两个Key的大小 
    public class ConfigurationKeyComparer : IComparer<string>
    {
        private const char KeyDelimiter = ':';

        public static ConfigurationKeyComparer Instance { get; } = new ConfigurationKeyComparer();

        internal static Comparison<string> Comparison { get; } = Instance.Compare;

        public int Compare(string? x, string? y)
        {
            ReadOnlySpan<char> xSpan = x.AsSpan();
            ReadOnlySpan<char> ySpan = y.AsSpan();
            //移除字符串前面的:
            xSpan = SkipAheadOnDelimiter(xSpan);
            ySpan = SkipAheadOnDelimiter(ySpan);

            // Compare each part until we get two parts that are not equal
            while (!xSpan.IsEmpty && !ySpan.IsEmpty)
            {
                int xDelimiterIndex = xSpan.IndexOf(KeyDelimiter);
                int yDelimiterIndex = ySpan.IndexOf(KeyDelimiter);

                int compareResult = Compare(
                    xDelimiterIndex == -1 ? xSpan : xSpan.Slice(0, xDelimiterIndex),
                    yDelimiterIndex == -1 ? ySpan : ySpan.Slice(0, yDelimiterIndex));

                if (compareResult != 0)
                {
                    return compareResult;
                }

                xSpan = xDelimiterIndex == -1 ? default :
                    SkipAheadOnDelimiter(xSpan.Slice(xDelimiterIndex + 1));
                ySpan = yDelimiterIndex == -1 ? default :
                    SkipAheadOnDelimiter(ySpan.Slice(yDelimiterIndex + 1));
            }

            return xSpan.IsEmpty ? (ySpan.IsEmpty ? 0 : -1) : 1;

            static ReadOnlySpan<char> SkipAheadOnDelimiter(ReadOnlySpan<char> a)
            {
                while (!a.IsEmpty && a[0] == KeyDelimiter)
                {
                    a = a.Slice(1);
                }
                return a;
            }
            //比较两个字符串的大小
            static int Compare(ReadOnlySpan<char> a, ReadOnlySpan<char> b)
            {
#if NETCOREAPP
                //先尝试将字符串转换为int
                bool aIsInt = int.TryParse(a, out int value1);
                bool bIsInt = int.TryParse(b, out int value2);
#else
                bool aIsInt = int.TryParse(a.ToString(), out int value1);
                bool bIsInt = int.TryParse(b.ToString(), out int value2);
#endif
                int result;

                if (!aIsInt && !bIsInt)
                {
                    // Both are strings
                    result = a.CompareTo(b, StringComparison.OrdinalIgnoreCase);
                }
                else if (aIsInt && bIsInt)
                {
                    // Both are int
                    result = value1 - value2;
                }
                else
                {
                    // Only one of them is int
                    result = aIsInt ? -1 : 1;
                }

                return result;
            }
        }
    }

    //一个空的Disposable子类
    internal sealed class EmptyDisposable : IDisposable
    {
        public static EmptyDisposable Instance { get; } = new EmptyDisposable();

        private EmptyDisposable()
        {
        }

        public void Dispose()
        {
        }
    }

    public static class ChangeToken
    {
        public static IDisposable OnChange(Func<IChangeToken?> changeTokenProducer, Action changeTokenConsumer)
        {
            if (changeTokenProducer is null)
            {
                //ThrowHelper.ThrowArgumentNullException(ExceptionArgument.changeTokenProducer);
            }
            if (changeTokenConsumer is null)
            {
                //ThrowHelper.ThrowArgumentNullException(ExceptionArgument.changeTokenConsumer);
            }

            return new ChangeTokenRegistration<Action>(changeTokenProducer, callback => callback(), changeTokenConsumer);
        }

        public static IDisposable OnChange<TState>(Func<IChangeToken?> changeTokenProducer, Action<TState> changeTokenConsumer, TState state)
        {
            if (changeTokenProducer is null)
            {
                //ThrowHelper.ThrowArgumentNullException(ExceptionArgument.changeTokenProducer);
            }
            if (changeTokenConsumer is null)
            {
                //ThrowHelper.ThrowArgumentNullException(ExceptionArgument.changeTokenConsumer);
            }

            return new ChangeTokenRegistration<TState>(changeTokenProducer, changeTokenConsumer, state);
        }

        private sealed class ChangeTokenRegistration<TState> : IDisposable
        {
            private readonly Func<IChangeToken?> _changeTokenProducer;
            private readonly Action<TState> _changeTokenConsumer;
            private readonly TState _state;
            private IDisposable? _disposable;

            private static readonly NoopDisposable _disposedSentinel = new NoopDisposable();

            public ChangeTokenRegistration(Func<IChangeToken?> changeTokenProducer, Action<TState> changeTokenConsumer, TState state)
            {
                _changeTokenProducer = changeTokenProducer;
                _changeTokenConsumer = changeTokenConsumer;
                _state = state;

                IChangeToken? token = changeTokenProducer();

                RegisterChangeTokenCallback(token);
            }

            private void OnChangeTokenFired()
            {
                IChangeToken? token = _changeTokenProducer();

                try
                {
                    _changeTokenConsumer(_state);
                }
                finally
                {
                    RegisterChangeTokenCallback(token);
                }
            }

            private void RegisterChangeTokenCallback(IChangeToken? token)
            {
                if (token is null)
                {
                    return;
                }
                IDisposable registraton = token.RegisterChangeCallback(s => ((ChangeTokenRegistration<TState>?)s)!.OnChangeTokenFired(), this);
                if (token.HasChanged && token.ActiveChangeCallbacks)
                {
                    registraton?.Dispose();
                    return;
                }
                SetDisposable(registraton);
            }

            private void SetDisposable(IDisposable disposable)
            {
                // We don't want to transition from _disposedSentinel => anything since it's terminal
                // but we want to allow going from previously assigned disposable, to another
                // disposable.
                IDisposable? current = Volatile.Read(ref _disposable);

                // If Dispose was called, then immediately dispose the disposable
                if (current == _disposedSentinel)
                {
                    disposable.Dispose();
                    return;
                }

                // Otherwise, try to update the disposable
                IDisposable? previous = Interlocked.CompareExchange(ref _disposable, disposable, current);

                if (previous == _disposedSentinel)
                {
                    // The subscription was disposed so we dispose immediately and return
                    disposable.Dispose();
                }
                else if (previous == current)
                {
                    // We successfully assigned the _disposable field to disposable
                }
                else
                {
                    // Sets can never overlap with other SetDisposable calls so we should never get into this situation
                    throw new InvalidOperationException("Somebody else set the _disposable field");
                }
            }

            public void Dispose()
            {
                // If the previous value is disposable then dispose it, otherwise,
                // now we've set the disposed sentinel
                Interlocked.Exchange(ref _disposable, _disposedSentinel)?.Dispose();
            }

            private sealed class NoopDisposable : IDisposable
            {
                public void Dispose()
                {
                }
            }
        }
    }
}
